package cn.dansj.common.utils.lock;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.UUID;
import java.util.concurrent.*;

@AutoRegisterLock(value = "memory", noConnection = true)
public class MemoryLock extends AbstractLock {
    private static final Logger log = LoggerFactory.getLogger(MemoryLock.class);
    private static final Map<String, LockEntry> lockMap = new ConcurrentHashMap<>();
    private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    private final String token;
    private volatile boolean locked = false;
    private ScheduledFuture<?> renewalTask;

    public MemoryLock(String lockName, long tryLockWaitMs, long lockExpireMs, boolean autoLock) {
        super(lockName, tryLockWaitMs, lockExpireMs, autoLock, null);
        this.token = UUID.randomUUID().toString();
    }

    @Override
    public boolean lock() throws LockException {
        if (locked) return false;

        final long startTime = System.currentTimeMillis();
        while (true) {
            synchronized (lockMap) {
                LockEntry entry = lockMap.get(lockName);
                if (entry == null || entry.isExpired()) {
                    // 创建新锁条目
                    long expireTime = lockExpireTimeMs > 0 ?
                            System.currentTimeMillis() + lockExpireTimeMs : Long.MAX_VALUE;
                    lockMap.put(lockName, new LockEntry(token, expireTime));
                    locked = true;
                    startRenewalTask();
                    return true;
                }
            }

            // 等待锁释放或超时
            if (System.currentTimeMillis() - startTime >= tryGetLockWaitMaxMs) {
                throw new LockException("Acquire lock timeout: " + lockName);
            }
            try {
                TimeUnit.MILLISECONDS.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                return false;
            }
        }
    }

    @Override
    public boolean unlock() {
        if (!locked) return false;

        synchronized (lockMap) {
            LockEntry entry = lockMap.get(lockName);
            if (entry != null && entry.token.equals(token)) {
                lockMap.remove(lockName);
                locked = false;
                stopRenewalTask();
                return true;
            }
        }
        return false;
    }

    private void startRenewalTask() {
        if (lockExpireTimeMs <= 0) return;

        renewalTask = scheduler.scheduleAtFixedRate(() -> {
            synchronized (lockMap) {
                LockEntry entry = lockMap.get(lockName);
                if (entry != null && entry.token.equals(token)) {
                    entry.expireTime = System.currentTimeMillis() + lockExpireTimeMs;
                    log.debug("Lock {} renewed", lockName);
                }
            }
        }, lockExpireTimeMs / 2, lockExpireTimeMs / 2, TimeUnit.MILLISECONDS);
    }

    private void stopRenewalTask() {
        if (renewalTask != null) {
            renewalTask.cancel(true);
        }
    }

    private static class LockEntry {
        final String token;
        volatile long expireTime;

        LockEntry(String token, long expireTime) {
            this.token = token;
            this.expireTime = expireTime;
        }

        boolean isExpired() {
            return System.currentTimeMillis() > expireTime;
        }
    }
}